package com.neusoft.neuiotms.dcvm.service.impl;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

import org.apache.commons.lang3.time.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.neusoft.bizcore.web.dto.result.ResultDTO;
import com.neusoft.bizcore.web.dto.result.ResultListDTO;
import com.neusoft.bizcore.web.utils.JsonUtils;
import com.neusoft.neuiotms.dcvm.bean.AttrBean;
import com.neusoft.neuiotms.dcvm.bean.DatabusCommandBean;
import com.neusoft.neuiotms.dcvm.bean.DatabusCommandBean.Metric;
import com.neusoft.neuiotms.dcvm.bean.MonitorDataBean;
import com.neusoft.neuiotms.dcvm.bean.StandardCiBean;
import com.neusoft.neuiotms.dcvm.converter.AlarmConverter;
import com.neusoft.neuiotms.dcvm.dto.AlarmDTO;
import com.neusoft.neuiotms.dcvm.model.Alarm;
import com.neusoft.neuiotms.dcvm.repository.AlarmRepository;
import com.neusoft.neuiotms.dcvm.service.AssetService;
import com.neusoft.neuiotms.dcvm.service.DatabusService;
import com.neusoft.neuiotms.dcvm.utils.DatabusApi;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@Service
public class DatabusServiceImpl implements DatabusService {

    @Autowired
    private RestTemplate restTemplate;
    @Value("${databus.target-url}")
    private String databusServiceUrl;
    @Autowired
    private AssetService assetService;
    @Autowired
    private AlarmRepository alarmRepository;
    @Autowired
    private AlarmConverter alarmConverter;

    @Override
    public MonitorDataBean getMonitorData(final String assetCode) {
        final ParameterizedTypeReference<ResultDTO<MonitorDataBean>> typeRef =
                new ParameterizedTypeReference<ResultDTO<MonitorDataBean>>() {
                };

        final ResponseEntity<ResultDTO<MonitorDataBean>> response = this.restTemplate
                .exchange(this.databusServiceUrl + String.format(DatabusApi.realtimeDataUrl, assetCode), HttpMethod.GET,
                        null,
                        typeRef);

        final ResultDTO<MonitorDataBean> result = response.getBody();

        final MonitorDataBean data = result.getData();
        data.setCode(assetCode);
        return data;
    }

    @Override
    public List<MonitorDataBean> getMonitorDatum(final String categoryCode) {
        final List<StandardCiBean> beans = this.assetService.getAllCiByCatetories(categoryCode);

        final ParameterizedTypeReference<ResultListDTO<MonitorDataBean>> typeRef =
                new ParameterizedTypeReference<ResultListDTO<MonitorDataBean>>() {
                };

        final List<String> resources = beans.stream().map(it -> it.getCode()).collect(Collectors.toList());

        final HttpEntity<List<String>> request = new HttpEntity<>(resources);
        final ResponseEntity<ResultListDTO<MonitorDataBean>> response = this.restTemplate
                .exchange(this.databusServiceUrl + DatabusApi.realtimeDatumUrl, HttpMethod.POST,
                        request,
                        typeRef);

        final List<MonitorDataBean> data =
                response.getBody().getData().stream().filter(it -> null != it.getData()).collect(Collectors.toList());
        return data;
    }

    @Override
    public List<MonitorDataBean> getMonitorDatum(final String categoryCode, final String loacation) {
        final List<StandardCiBean> beans = this.assetService.getAllCiByCatetories(categoryCode);

        final ParameterizedTypeReference<ResultListDTO<MonitorDataBean>> typeRef =
                new ParameterizedTypeReference<ResultListDTO<MonitorDataBean>>() {
                };

        final String[] locations = loacation.split(",");

        final List<String> resources =
                beans.stream()
                        .filter(it -> it.getAttrs().stream()
                                .anyMatch(attr -> Arrays.asList(locations).contains(attr.getValue())))
                        .map(it -> it.getCode()).collect(Collectors.toList());

        final HttpEntity<List<String>> request = new HttpEntity<>(resources);
        final ResponseEntity<ResultListDTO<MonitorDataBean>> response = this.restTemplate
                .exchange(this.databusServiceUrl + DatabusApi.realtimeDatumUrl, HttpMethod.POST,
                        request,
                        typeRef);

        final List<MonitorDataBean> data =
                response.getBody().getData().stream().filter(it -> null != it.getData()).collect(Collectors.toList());
        return data;
    }

    @Override
    public void receiveRedisAlarmMessage(final String message) {
        final Map<String, Object> data = JsonUtils.jsonToMap(message);
        final String type = (String) data.get("type");
        final String alarmId = (String) data.get("alarmId");
        if ("2".equals(type)) {
            final Alarm model = this.alarmRepository.findOneByAlarmId(alarmId);
            if (null != model) {
                this.alarmRepository.delete(model);
            }
            return;
        }

        final String alarmTime = (String) data.get("alarmTime");
        final String alarmLevel = (String) data.get("alarmLevel");
        final String alarmContent = (String) data.get("alarmContent");
        final String assetCode = (String) data.get("assetCode");

        final AlarmDTO alarm = new AlarmDTO();
        alarm.setSourceType(1);
        alarm.setAlarmId(alarmId);
        alarm.setAlarmLevel(alarmLevel);
        alarm.setAlarmContent(alarmContent);

        try {

            final Date time = DateUtils.parseDate(alarmTime, "yyyy-MM-dd HH:mm:ss");
            alarm.setAlarmTime(time);
        } catch (final Exception e) {
            DatabusServiceImpl.log.warn("", e);
            alarm.setAlarmTime(new Date());
        }
        alarm.setAssetCode(assetCode);

        try {
            final StandardCiBean alarmAssetCi = this.assetService.getCiDetail(assetCode);
            alarm.setAssetName(alarmAssetCi.getName());
            final Optional<AttrBean> alarmAssetLocation =
                    alarmAssetCi.getAttrs().stream().filter(attr -> "location".equals(attr.getKey())).findAny();

            if (alarmAssetLocation.isPresent()) {
                final StandardCiBean locationCi =
                        this.assetService.getCiDetail(String.valueOf(alarmAssetLocation.get().getValue()));
                alarm.setLocationCode(locationCi.getCode());
                alarm.setLocationName(locationCi.getName());
            }

            final Optional<AttrBean> cabinetAttr =
                    alarmAssetCi.getAttrs().stream().filter(it -> "cabinet".equals(it.getKey())).findAny();
            if (cabinetAttr.isPresent()) {
                alarm.setCabinetCode((String) cabinetAttr.get().getValue());

                try {
                    final StandardCiBean cabinetCi = this.assetService.getCiDetail(alarm.getCabinetCode());
                    alarm.setCabinetName(cabinetCi.getName());
                    final Optional<AttrBean> cabinetLocation =
                            cabinetCi.getAttrs().stream().filter(attr -> "location".equals(attr.getKey())).findAny();

                    if (cabinetLocation.isPresent()) {
                        final StandardCiBean locationCi =
                                this.assetService.getCiDetail(String.valueOf(cabinetLocation.get().getValue()));
                        alarm.setLocationCode(locationCi.getCode());
                        alarm.setLocationName(locationCi.getName());
                    }

                } catch (final Exception e) {
                }

            }

        } catch (final Exception e) {
            DatabusServiceImpl.log.error("", e);
        }
        final Alarm model = this.alarmConverter.toModel(alarm);
        try {
            this.alarmRepository.save(model);
        } catch (final Exception e) {
            DatabusServiceImpl.log.error("", e);
        }

    }

    @SuppressWarnings("unchecked")
    @Override
    public List<Object> getMenjinRecord(final String resourceCode) {
        final ParameterizedTypeReference<ResultDTO<DatabusCommandBean>> typeRef =
                new ParameterizedTypeReference<ResultDTO<DatabusCommandBean>>() {
                };

        final ResponseEntity<ResultDTO<DatabusCommandBean>> response = this.restTemplate
                .exchange(this.databusServiceUrl + String.format(DatabusApi.resourceCollectUrl, resourceCode),
                        HttpMethod.GET,
                        null,
                        typeRef);

        final ResultDTO<DatabusCommandBean> result = response.getBody();

        final DatabusCommandBean data = result.getData();
        final Metric metric = data.getResourceBean().get(0).getMetrics().get(0);
        final String json = metric.getValue();
        final Map<String, Object> mapRes = JsonUtils.jsonToMap(json);
        if (mapRes.containsKey("success") && "true".equals(String.valueOf(mapRes.get("success")))) {
            final Map<String, Object> dataItem = (Map<String, Object>) mapRes.get("data");
            final List<Object> pageData = (List<Object>) dataItem.get("pageData");
            return pageData;

        } else {
            return new ArrayList<>();
        }

    }

    @Override
    public Map<String, Object> fetchMonitorData(String assetCode, Map<String, Object> params) {
        final ParameterizedTypeReference<ResultDTO<DatabusCommandBean>> typeRef =
                new ParameterizedTypeReference<ResultDTO<DatabusCommandBean>>() {
                };

        String url = this.databusServiceUrl + String.format(DatabusApi.resourceCollectUrl, assetCode);
        if (null != params) {
            final List<String> queryList = Lists.newArrayList();
            params.entrySet().forEach(entry -> {
                final String param = entry.getKey() + "=" + entry.getValue();
                queryList.add(param);
            });
            if (queryList.size() > 0) {
                final String queryStr = String.join("&", queryList);
                url += "?" + queryStr;
            }
        }

        final ResponseEntity<ResultDTO<DatabusCommandBean>> response =
                this.restTemplate.exchange(url, HttpMethod.GET, null, typeRef);

        final ResultDTO<DatabusCommandBean> result = response.getBody();

        final DatabusCommandBean data = result.getData();
        final List<Metric> metrics = data.getResourceBean().get(0).getMetrics();

        final List<Map<String, Object>> metricData = metrics.stream()
                .map(it -> {
                    final Map<String, Object> map = new HashMap<>();
                    map.put("name", it.getName());
                    map.put("value", it.getValue());
                    map.put("code", it.getVariable());
                    if (null != it.getUnit()) {
                        map.put("unit", it.getUnit());
                    }
                    return map;
                }).collect(Collectors.toList());
        final Map<String, Object> monitor = Maps.newHashMap();
        monitor.put("collectedTime", new Date());
        monitor.put("elapsedTime", 0);
        monitor.put("data", metricData);

        return monitor;
    }

}
